﻿
Imports System.Net.NetworkInformation
Imports dpslib
Imports remotecall

Public Module machinehealth
    '=0, success
    '<0, warning
    '>0, serious error
    Public Class failure
        Public Const success As Int32 = 0
        Public Const deploys As Int32 = 1
        Public Const outwardNetwork As Int32 = -1
        Public Const internalNetwork As Int32 = 2
        Public Const harddisk As Int32 = 3
        Public Const memory As Int32 = 4
        Public Const cpuUsage As Int32 = 5
    End Class

    Private ReadOnly getPerfCounterLocally As Boolean = True

    Sub New()
        getPerfCounterLocally = Not existingInstance(perfcounter.instanceName)
        raiseError("get performance counter locally set to ", getPerfCounterLocally)
    End Sub

    'check whether there is a valid deploys process
    Private Function checkDeploys() As Boolean
        Const findprocess As String = "deploys"
        Dim procs() As Process = Nothing
        procs = Process.GetProcessesByName(findprocess)
        Return Not procs Is Nothing AndAlso procs.Length() > 0
    End Function

    'check outward network
    Private Function checkOutwardNetwork() As Boolean
        Return pingHttpWeb("http://example.net/")
    End Function

    'check internal network
    Private Function checkInternalNetwork() As Boolean
        If netstat.networkConnections().Length() = 0 Then
            raiseError(errorHandle.errorType.warning, "no network connections")
            Return False
        End If

        Dim p As Ping = Nothing
        p = New Ping()
        Dim reply As PingReply = Nothing
        For i As Int64 = 0 To netstat.networkConnections().Length() - 1
            If netstat.networkConnections()(i).ipEnabled() AndAlso _
               Not isemptystring(netstat.networkConnections()(i).defaultIpGateway()) Then
                reply = p.Send(netstat.networkConnections()(i).defaultIpGateway())
                If Not reply.Status = IPStatus.Success Then
                    raiseError(errorHandle.errorType.warning, _
                               "network connection ", i, " error, cannot access getway ", _
                               netstat.networkConnections()(i).defaultIpGateway())
                    Return False
                Else
                    raiseError("network connection ", i, " status ok, ", _
                               netstat.networkConnections()(i).ipAddress(), " ", _
                               netstat.networkConnections()(i).ipSubnet(), " ", _
                               netstat.networkConnections()(i).defaultIpGateway())
                End If
            End If
        Next

        Return True
    End Function

    'check harddisk
    Private Function checkHarddisk() As Boolean
        Dim dirs() As String = Nothing
        dirs = listdirectories(filesystem.deployRoot)
        Return Not dirs Is Nothing AndAlso dirs.Length() > 0
    End Function

    'check memory
    Private Function checkMemory() As Boolean
        Return availablePhysicalMemory() / totalPhysicalMemory() >= 0.1 OrElse _
               availableVirtualMemory() / totalVirtualMemory() >= 0.2
    End Function

    Private Function readCpuUsage(ByRef usage As Single) As Boolean
        If getPerfCounterLocally Then
            Dim cur As Single = 0
            usage = 0
            For i As Int64 = 0 To 3
                If readPerformanceCounterValue(performanceCounterCategories.processor, _
                                               performanceCounterCounters.processorTime, _
                                               performanceCounterInstances.total, _
                                               cur) AndAlso cur > usage Then
                    usage = cur
                End If
            Next

            Dim mhUsage As Single
            Dim proc As Process = Nothing
            proc = Process.GetCurrentProcess()
            mhUsage = proc.TotalProcessorTime().Ticks() / (Now.Ticks() - proc.StartTime().Ticks()) / _
                      Environment.ProcessorCount() * 100
            raiseError("total processor usage ", usage, ", current process processor usage ", mhUsage)
            usage -= mhUsage
            Return True
        Else
            Dim rtn As bytesString = Nothing
            rtn = rcclient.invoke(Of bytesString)(perfcounter.cpuusage)
            If rtn Is Nothing Then
                Return False
            Else
                Return Single.TryParse(rtn, usage)
            End If
        End If
    End Function

    'check CPU usage
    Private Function checkCPUUsage() As Boolean
        Dim usage As Single = 0
        If Not readCpuUsage(usage) Then
            raiseError(errorHandle.errorType.warning, "cannot read processor performance counter")
            Return False
        Else
            raiseError("cpu usage ", usage)
            Return usage < 90
        End If
    End Function

    Public Sub main(ByVal args() As String)
        startmain(Nothing, errorHandle.NOLOGFILEMARK)
        counter.initial(counter.NOCOUNTERFILEMARK)
        If Not getPerfCounterLocally Then
            rcclient.initial(network.localhost, perfcounter.port)
        End If
        Dim rtn As Int32 = failure.success
        Try
            If Not checkDeploys() Then
                rtn = failure.deploys
                GoTo finish
            End If
            sleepSeconds(1)
            If Not checkInternalNetwork() Then
                rtn = failure.internalNetwork
                GoTo finish
            End If
            sleepSeconds(1)
            If Not checkHarddisk() Then
                rtn = failure.harddisk
                GoTo finish
            End If
            sleepSeconds(1)
            If Not checkMemory() Then
                rtn = failure.memory
                GoTo finish
            End If
            sleepSeconds(1)
            If Not checkCPUUsage() Then
                rtn = failure.cpuUsage
                GoTo finish
            End If
            sleepSeconds(1)

            ' optional, and return -1, which is not treated as error
            If Not checkOutwardNetwork() Then
                rtn = failure.outwardNetwork
                GoTo finish
            End If
        Catch ex As Exception
            raiseError(errorHandle.errorType.critical, "internal error ", ex.Message(), ", callstack ", ex.StackTrace())
        End Try

finish:
        If Not getPerfCounterLocally Then
            rcclient.close()
        End If
        finishmain()
        forceQuit(rtn)
    End Sub
End Module
